import {ObjectInformation, ObjectSizeInformation} from "../event-types";


export function contains(parent: ObjectInformation, elements: ObjectInformation[]): ObjectSizeInformation[] {
    const allElements = [parent, ...elements];
    const rectangles = toRect(allElements);
    const parentRectangle = rectangles.shift();
    // @ts-ignore
    return rectangles.filter(r => containsRect(parentRectangle.rect, r.rect)).map(r => r.entry);
}

export function toRect(elements: ObjectInformation[]): RectangleObject[] {
    return elements.map(d => {
        return {
            entry: d,
            rect: {
                x1: d.x,
                y1: d.y,
                x2: d.x + d.width,
                y2: d.y + d.height
            },
            contains: []
        }
    });
}

export function ToTree(elements: ObjectInformation[]): TreeItem {
    const rectangles = toRect(elements);
    // find the root node. The root node is the object that contains every other node including itself
    const rootCandidates = roots(rectangles);
    if(rootCandidates.length > 1 || rootCandidates.length === 0) {
        throw new Error();
    }
    const root = rootCandidates[0];
    if(!root) {
        throw new Error();
    }
    const allChildren = rectangles.filter(e => e != root);

    return ToTreeItem(root, allChildren);
}

function ToTreeItem(root: RectangleObject, allChildren: RectangleObject[]): TreeItem {
    const directChildren = roots(allChildren);
    return {
        rectangle: root.rect,
        object: root.entry, // Create children
        children: directChildren.map(c => ToTreeItem(c, allChildren.filter(ac => containsRect(c.rect, ac.rect) && ac != c)))
    }
}

export function roots(rectangles: RectangleObject[]): RectangleObject[] {
    // All rectangles that are only contained by themselves
    return rectangles.filter(e => rectangles.filter(r => containsRect(r.rect, e.rect)).length === 1)
}

export function ToArray(tree: TreeItem): ObjectInformation[] {
    const children = tree.children.map(c => ToArray(c)).flat();
    return [tree.object, ...children];
}

export function OnEachItem(tree: TreeItem, action: (item: TreeItem) => void) {
    action(tree);
    for (const child of tree.children) {
        OnEachItem(child, action);
    }
}

export async function OnEachItemAsync(tree: TreeItem, action: (item: TreeItem) => Promise<void>) {
    await action(tree);
    for (const child of tree.children) {
        await OnEachItemAsync(child, action);
    }
}


export const containsRect = (r1: Rectangle, r2: Rectangle) => {
    return  r1.x1 <= r2.x1 && r1.y1 <= r2.y1 && r1.x2 >= r2.x2 && r1.y2 >= r2.y2
}

export interface Rectangle {
    x1: number;
    x2: number;
    y1: number;
    y2: number;
}

export interface RectangleObject {
    entry: ObjectInformation,
    rect: Rectangle,
    contains: RectangleObject[]
}

export interface TreeItem {
    rectangle: Rectangle;
    object: ObjectInformation;
    children: TreeItem[];
}